/*
solchars table
Used to make characters able to turn into their sol form.
Parameters for characters are as follows:
Sol color - The super color to use for this character's sol form, set to nil to just use super color.
Sol status - Set to 1 to prevent turning super. Set to 2 to allow turning super. 3 allows turning super and does not apply the sol color ramp.
To use in your own mod, copy the format seen below.

if not solchars
    rawset(_G, "solchars", {})
end
solchars["example"] = {SKINCOLOR_BURNING1, 1}
*/

if not solchars
    rawset(_G, "solchars", {})
end
solchars["blaze"] = {SKINCOLOR_BURNING1, 1}
solchars["marine"] = {SKINCOLOR_AQUAMARINE1, 1}

-- new way to determine how many sol emeralds a player has
rawset(_G, "mpemeraldsave", 0)
addHook("NetVars", function(net)
	-- make sure this table is synced in netgames
	mpemeraldsave = net($)
end)

local function hasSolEmerald(p, emeraldnum)
	-- check if they permanently have the emerald
	if (p.solemeralds & (1 << (emeraldnum - 1)))	-- they permanently have it!
	or (mpemeraldsave & (1 << (emeraldnum - 1)))	-- they got it temporarily in MP
		return true
	end
	
	-- they don't have it
	return false
end
local function allSolEmeralds(p)
	local emeralds = 0
	for i = 1, 7
		if not (hasSolEmerald(p, i)) then continue end
		emeralds = $|(1 << (i - 1))
	end
	
	return All7Emeralds(emeralds)
end
	
local solemerald = "client/rush/esav.dat"
COM_AddCommand("resetsolemeralddata", function(p, arg)
	if arg and (arg == "yes")
		CONS_Printf(p, "Sol Emeralds have been reset. Blaze will remember this.")
		p.solemeralds = 0
		if p == consoleplayer or p == secondarydisplayplayer
			local file = io.openlocal(solemerald, "w+")
			file:write("0")
			file:close()
		end
	else
		CONS_Printf(p, "Resets your Sol Emeralds. Please append \"yes\" to this command in order to excecute it.")
	end
end)

local offx = {-60, -40, -20, 0, 20, 40, 60}
local offy = {26, 12, 4, 0, 4, 12, 26}
local offx2 = {-63, -54, -45, -36, -27, -18, -9}
local offy2 = {0, 0, 0, 0, 0, 0, 0}
hud.add(function(v)
    if consoleplayer
		if not multiplayer
			for i = 1, 7
				if (consoleplayer.solemeralds & (1 << (i-1)))
					local emerald = v.cachePatch("SEM"..i.."A0")
					local xx = offx[i]
					local yy = offy[i]
					v.draw(160+xx ,25+yy,emerald)
				end
			end
		elseif gametyperules & GTR_CAMPAIGN --CBW
			for i = 1, 7
				if (consoleplayer.solemeralds & (1 << (i-1)))
					local emerald = v.cachePatch("TSEM"..i)
					local xx = offx2[i]-i
					local yy = offy2[i]
					v.draw(158+xx ,18+yy,emerald)
				elseif (mpemeraldsave & (1 << (i - 1)))	-- show a transparent version of a temporary sol emerald
				and (netgame)	-- only in netgames!
					local emerald = v.cachePatch("TSEM"..i)
					local xx = offx2[i]-i
					local yy = offy2[i]
					v.draw(158+xx, 18+yy, emerald, V_TRANSLUCENT)
				end
			end
		end
    end
end, "scores")

-- Every solchar player not damaged when a boss is killed gets a sol emerald! - snu
addHook("MapLoad", function(mapnum)
	for p in players.iterate
		p.washitfromsolemerald = false
		p.solemeraldskincheese = false
	end
end)
addHook("MobjDamage", function(mo, inflictor, source)
	mo.player.washitfromsolemerald = true	-- missed your chance!
end, MT_PLAYER)

-- objects that give sol emeralds ([mobj number] = sol emerald number,) - snu
local solemerald_mobjs = {
	[MT_EGGMOBILE] = 1,
	[MT_EGGMOBILE2] = 2,
	[MT_EGGMOBILE3] = 3,
	[MT_EGGMOBILE4] = 4,
	[MT_FANG] = 5,
	[MT_METALSONIC_BATTLE] = 6,
	[MT_CYBRAKDEMON] = 7,
}
for k, v in pairs(solemerald_mobjs)
	addHook("MobjDeath", function(target, inflictor, source)
		if (modeattacking) then return end	-- don't obtain them in record attack!
		
		local emeraldnum = solemerald_mobjs[target.type]
		if (splitscreen)
			if (consoleplayer.solemeralds & (1 << (emeraldnum - 1))) then return end	-- we already have it!

			-- special splitscreen handling!
			local skincheese = 0
			local notdamaged = true
			local solcharfound = false
			for p in players.iterate
				if (p.solemeraldskincheese)
					skincheese = $+1
					if (skincheese == 2)
						return
					end
				end
				
				-- BOTH players need to not get hit
				if (p.washitfromsolemerald)
					notdamaged = false
				end
				
				-- we need at least 1 solchar present
				if (solchars[p.mo.skin])
					solcharfound = true
				end
			end
			if not (notdamaged and solcharfound) then return end
			
			-- spawn the emerald for the splitscreen players!
			local emeraldstate = "S_SOLEMERALD"..emeraldnum
			local emerald = P_SpawnMobjFromMobj(target, 0, 0, 0, MT_SOLEMERALD)
			emerald.state = _G[emeraldstate]
			emerald.emeraldnum = emeraldnum	-- makes things a lot easier
			emerald.target = source
			return
		end
		
		-- sol emerald spawning conditions
		if (netgame)	-- as long as one player doesn't get hit
			if (mpemeraldsave & (1 << (emeraldnum - 1))) then return end	-- we already have this one in the session!
			
			-- any character can obtain a sol emerald in a netgame!
			local foundplayer = false
			for p in players.iterate
				if (p.bot) then continue end	-- not for bots
				if (p.washitfromsolemerald) then continue end	-- they got hit
				
				-- we found a player that qualifies
				foundplayer = true
				break
			end
			if not (foundplayer) then return end 	-- nobody qualified, missed out on the sol emerald!
			
			-- now spawn the emerald for everyone
			for p in players.iterate
				-- spawn an emerald for that player!
				local emeraldstate = "S_SOLEMERALD"..emeraldnum
				local emerald = P_SpawnMobjFromMobj(target, 0, 0, 0, MT_SOLEMERALD)
				emerald.state = _G[emeraldstate]
				emerald.emeraldnum = emeraldnum	-- makes things a lot easier
				emerald.target = p.mo
				if (p != consoleplayer)
					emerald.flags2 = $|MF2_DONTDRAW
				end
			end
		else	-- only the consoleplayer needs to not get hit
			local p = consoleplayer	-- usually I'd be murdered for this... but it's not a netgame!
			
			if (p.solemeraldskincheese) then return end	-- gonna keep this here
			if (p.washitfromsolemerald) then return end	-- was hit
			if not (solchars[p.mo.skin]) and not netgame then return end	-- can't get them when not a solchar
			if (p.solemeralds & (1 << (emeraldnum - 1))) then return end	-- we already have it!
			
			-- spawn the emerald!
			local emeraldstate = "S_SOLEMERALD"..emeraldnum
			local emerald = P_SpawnMobjFromMobj(target, 0, 0, 0, MT_SOLEMERALD)
			emerald.state = _G[emeraldstate]
			emerald.emeraldnum = emeraldnum	-- makes things a lot easier
			emerald.target = p.mo
		end
	end, k)
end

-- get the sol emerald from the object! - snu
local function giveSolEmerald(p, emerlnum)
	if netgame--piss off you're not saving this in netgames
		mpemeraldsave = $ | (1 << (emerlnum - 1))
		
		-- display the necessary message
		if not (p.solemeralds & (1 << (emerlnum - 1)))
			-- additional message to indicate the temporary state
			CONS_Printf(p, "\136You got a Sol Emerald, but only temporarily.")
			CONS_Printf(p, "\136To keep it permanently, obtain it in Singleplayer!")
		else
			CONS_Printf(p, "\136You got a Sol Emerald!")
		end
	else
		p.solemeralds = $ | (1 << (emerlnum - 1))
		if p == consoleplayer or p == secondarydisplayplayer
			local file = io.openlocal(solemerald, "w+")
			file:write(p.solemeralds)
			file:close()
		end
		CONS_Printf(p, "\136You got a Sol Emerald!")
	end
	S_StartSound(nil, sfx_solemr, p)
end
addHook("TouchSpecial", function(emerald, blaze)
	if not (blaze and blaze.valid) then return true end
	if (blaze != emerald.target) then return true end	-- not the clientsided target
	
	local emerlnum = emerald.state - S_SOLEMERALD1 + 1
	giveSolEmerald(blaze.player, emerlnum)
	-- make sure this is set for the other player in splitscreen!
	if (splitscreen)
		local splitplayer = (blaze.player == consoleplayer) and secondarydisplayplayer or consoleplayer
		splitplayer.solemeralds = $ | (1 << (emerlnum - 1))
	end
end, MT_SOLEMERALD)

-- sol emerald thinker - snu
local bullshit = {
[S_SOLEMERALD1] = SKINCOLOR_MINT,
[S_SOLEMERALD2] = SKINCOLOR_BUBBLEGUM,
[S_SOLEMERALD3] = SKINCOLOR_SAPPHIRE,
[S_SOLEMERALD4] = SKINCOLOR_SKY,
[S_SOLEMERALD5] = SKINCOLOR_ORANGE,
[S_SOLEMERALD6] = SKINCOLOR_SALMON,
[S_SOLEMERALD7] = SKINCOLOR_AETHER,
}

addHook("MobjThinker", function(mo)
	if not ((mo and mo.valid) and mo.health) then return end
	
	-- target handling
	local target = mo.target
	if not (target and target.valid)
		-- attempt to give a lingering player the emerald?
		local ptarget = mo.playertarget
		if (ptarget and ptarget.valid)
			giveSolEmerald(ptarget, mo.emeraldnum)
			-- make sure this is set for the other player in splitscreen!
			if (splitscreen)
				local splitplayer = (ptarget == consoleplayer) and secondarydisplayplayer or consoleplayer
				splitplayer.solemeralds = $ | (1 << (emerlnum - 1))
			end
		end
		
		-- we did all we could
		P_RemoveMobj(mo)
		return
	else
		-- ... ?!
		if not (target.player)	-- ?!
			P_RemoveMobj(mo)
			return
		end
		
		-- they died AFTER they got it? ...just give them the emerald
		if not (target.health)
			giveSolEmerald(target.player, mo.emeraldnum)
			-- make sure this is set for the other player in splitscreen!
			if (splitscreen)
				local splitplayer = (target.player == consoleplayer) and secondarydisplayplayer or consoleplayer
				splitplayer.solemeralds = $ | (1 << (emerlnum - 1))
			end
			
			P_RemoveMobj(mo)
			return
		end
		
		-- attempt to home in on target!
		if (mo.homeinwait == nil) then mo.homeinwait = 3*TICRATE end
		
		if (mo.homeinwait)
			-- bob up and down
			P_TeleportMove(mo, mo.x, mo.y, mo.z + (2 * cos(ANG10*leveltime)))
			
			-- decrease timer
			mo.homeinwait = $-1
		else
			local spdfac = 40*FRACUNIT
			local spdadd = 6*FRACUNIT
			
			local ang = R_PointToAngle2(mo.x, mo.y, target.x, target.y)
			local dist = FixedHypot(FixedHypot(target.x  - mo.x, target.y - mo.y), target.z - mo.z)
			dist = $ or 1
			
			local tmomx = FixedMul(FixedDiv(target.x - mo.x, dist), spdfac)
			local tmomy = FixedMul(FixedDiv(target.y - mo.y, dist), spdfac)
			local tmomz = FixedMul(FixedDiv(target.z - mo.z, dist), spdfac)
			
			-- now do bad stuff...
			if mo.momx > tmomx
				mo.momx = max(tmomx, $-spdadd*3/2)
			end    
			if mo.momy > tmomy
				mo.momy = max(tmomy, $-spdadd*3/2)
			end            
			if mo.momz > tmomz
				mo.momz = max(tmomz, $-spdadd/5)
			end            

			if mo.momx < tmomx
				mo.momx = min(tmomx, $+spdadd*3/2)
			end    
			if mo.momy < tmomy
				mo.momy = min(tmomy, $+spdadd*3/2)
			end            
			if mo.momz < tmomz
				mo.momz = min(tmomz, $+spdadd/5)
			end
			
			if (rusheffects.value) and not (leveltime%2)
				local ass = P_SpawnMobjFromMobj(mo, 0, 0, 0, MT_BOXSPARKLE)
				ass.scale = $*2
				ass.colorized = true
				ass.color = bullshit[mo.state]
				ass.frame = $ | FF_FULLBRIGHT
				if (target.player != consoleplayer)
				and not (splitscreen)	-- always visible here
					ass.flags2 = $|MF2_DONTDRAW
				end
			end
		end
		
		-- spawn particles
		if (rusheffects.value) and (P_RandomRange(1, 4) == 1)
			local ass = P_SpawnMobjFromMobj(mo, P_RandomRange(30, -30)*FRACUNIT, P_RandomRange(30, -30)*FRACUNIT, P_RandomRange(30, -30)*FRACUNIT, MT_BOXSPARKLE)
			ass.colorized = true
			ass.color = bullshit[mo.state]
			ass.frame = $ | FF_FULLBRIGHT
			if P_RandomRange(0, 2) != 0
				ass.scale = P_RandomRange(1,2)*FRACUNIT
			else
				ass.scale = FRACUNIT - FRACUNIT/4
			end
			
			if (target.player != consoleplayer)
			and not (splitscreen)	-- always visible here
				ass.flags2 = $|MF2_DONTDRAW
			end
		end
		
		-- save this
		mo.playertarget = mo.player
	end
end, MT_SOLEMERALD)

addHook("PlayerSpawn", function(p)
	if not (p and p.mo and p.mo.valid) return end
	p.solchar = nil
	if solchars[p.mo.skin]
		local bskin = skins[p.skin]
		p.normalspeed = bskin.normalspeed
		p.runspeed = bskin.runspeed
		p.shieldscale = bskin.shieldscale
		if not (bskin.flags & SF_RUNONWATER)
			p.charflags = $ & ~ SF_RUNONWATER
		end
		if not (bskin.flags & SF_NOSHIELDABILITY)
			p.charflags = $ & ~ SF_NOSHIELDABILITY
		end
		p.fakenormalspeed = bskin.normalspeed
		p.jumpfactor = bskin.jumpfactor
		if p.mo.skin == "blaze"
			p.mindash = bskin.mindash
			p.maxdash = bskin.maxdash
		end
	end
end)

addHook("PreThinkFrame", function(aeeaaeaeea)
	for p in players.iterate
		if not p.solchar
			local solchar = {
				istransformed = 0,
				burningcolor = 1,
				bcolordirection = 1,
				spindown = false,
				jumpdown = false,
				earlyburncancel = false,
				hasallemeralds = false,
				solexception = false,
				musicplaying = 0,
				emeraldcircle = 0
			}

			if p.mo and p.mo.valid and solchars[p.mo.skin]
				p.solchar = solchar
			else
				p.solchar = nil
			end
		end
		if p.solemeralds == nil
			p.solemeralds = 0
		end
	end
end)

--Sol emerald getting command
COM_AddCommand("silvaze", function(p, arg)
	if not p.solemeraldslockout
		p.solemeraldslockout = true
		if arg
			arg = tonumber($)
			p.solemeralds = arg and min(max(arg, 0), 127) or 0
		end
	elseif not CBW_Battle and p.mo and p.mo.valid and p.mo.health
		P_DamageMobj(p.mo, nil, nil, 1, DMG_INSTAKILL)
		print(p.name .. " has horrible taste.")
	end
end)

--Sol emerald I/O reading
addHook("PlayerSpawn", function(p)
	if not p.solemeraldslockout
	and (p == consoleplayer or p == secondarydisplayplayer)
		local file = io.openlocal(solemerald)--check for the file
		local num = 0
		if file
			num = file:read("*n") or $
			file:close()
		end
		
		if netgame
			COM_BufInsertText(p, "silvaze " + num)
		else
			p.solemeraldslockout = true
			p.solemeralds = min(max(num, 0), 127)
		end
	end
end)

addHook("PreThinkFrame", function(ae)
	for p in players.iterate
		if not (p.solchar and p.mo and solchars[p.mo.skin] and not p.powers[pw_super]) continue end
		if (modeattacking) continue end
		if (((p.cmd.buttons & BT_SPIN) and (p.pflags & PF_JUMPED) and not (p.powers[pw_shield]) and not rphys) or (rphys and (p.cmd.buttons & BT_ATTACK)))
		and (p.solchar.hasallemeralds and gametyperules & GTR_CAMPAIGN --CBW
			or not(gametyperules & GTR_CAMPAIGN) and (All7Emeralds(emeralds) or All7Emeralds(p.powers[pw_emeralds]))) --CBW
		and not (p.pflags & (PF_THOKKED|PF_SPINNING)) and not p.solchar.spindown
		and not p.powers[pw_carry] and (p.panim != PA_ABILITY) and (p.panim != PA_ABILITY2) and (p.panim != PA_PAIN) and not (maptol & TOL_NIGHTS)
		and not (p.solchar.istransformed) and not G_IsSpecialStage(gamemap)
			if p.rings >= 50
				p.solchar.istransformed = 1
				p.solchar.spindown = true
				p.mo.state = S_PLAY_SUPER_TRANS1
				S_StartSound(p.mo, sfx_secir)
				p.powers[pw_extralife] = 0
				p.powers[pw_sneakers] = 0
				p.powers[pw_invulnerability] = 0
				p.solchar.emeraldcircle = 1
			end
		end
		if p.solchar.istransformed == 1
			p.mo.momx = 0
			p.mo.momz = 0
			p.mo.momy = 0
			p.powers[pw_noautobrake] = 4
			p.powers[pw_ignorelatch] = 2+(1<<15)
			p.solchar.burningcolor = 5
			if p.rings == 0
				p.mo.state = S_PLAY_FALL
			end
		end
	end
end)

addHook("PlayerThink", function(p)
	if not (p.mo) then return end
	
	-- we need to hunt emeralds as a solchar! - snu
	if not (solchars[p.mo.skin])
		p.solemeraldskincheese = true
	end
	
	if not solchars[p.mo.skin] and p.solchar
		if (p.solchar.istransformed)
			p.solchar.istransformed = 0
			P_RestoreMusic(p)
			p.mo.eflags = $ & ~ MFE_FORCESUPER
			p.mo.state = $
		end
		p.solchar = nil
	end
	
	if solchars[p.mo.skin] and p.solchar
		//Skid animation
		if (p.mo.skin == "blaze" or p.mo.skin == "marine")
		and p.mo.state == S_PLAY_SKID
			local tics = p.mo.tics
			if not (tics%4)
				p.mo.state = S_PLAY_SKID
				p.mo.tics = tics
			end
		end
		
		if ((p.exiting) or (p.playerstate != PST_LIVE)) and p.solchar.istransformed
			local bskin = skins[p.skin]
			p.solchar.istransformed = 0
			p.solchar.solexception = false
			P_RestoreMusic(p)
			if not (bskin.flags & SF_RUNONWATER)
				p.charflags = $ & ~ SF_RUNONWATER
			end
			if not (bskin.flags & SF_NOSHIELDABILITY)
				p.charflags = $ & ~ SF_NOSHIELDABILITY
			end
			p.mo.eflags = $ & ~ MFE_FORCESUPER
			p.mo.state = $
			if not p.isxmomentum
				p.normalspeed = bskin.normalspeed
			end
			p.runspeed = bskin.runspeed
			p.shieldscale = bskin.shieldscale
			if p.mo.skin == "blaze"
				p.mindash = bskin.mindash
				p.maxdash = bskin.maxdash
			end
			p.mo.color = p.skincolor
			p.fakenormalspeed = bskin.normalspeed
			p.jumpfactor = bskin.jumpfactor
		end
		
		if p.mo.OkDoTheSoundNowPlease
			S_StartSound(nil, p.mo.OkDoTheSoundNowPlease)
			p.mo.OkDoTheSoundNowPlease = nil
		end
		
		if p.mo.quitplayinsoundsjackass and S_SoundPlaying(p.mo, sfx_secir)
			S_StopSoundByID(p.mo, sfx_secir)
			p.mo.quitplayinsoundsjackass = nil
		end
		
		p.CancelSolTransformAnimation = false
		p.CancelSolTransform = false
		if (p.SolTransforming)
			if (p.cmd.buttons & BT_JUMP) and not (p.solchar.jumpdown)
				p.CancelSolTransformAnimation = true
			elseif (p.cmd.buttons & BT_SPIN) and not (p.solchar.spindown) and solchars[p.mo.skin][2] >= 2 and All7Emeralds(emeralds)
				p.CancelSolTransform = true
				p.charflags = $|SF_SUPER
				p.mo.state = S_PLAY_STND
				P_DoSuperTransformation(p, false)
				p.mo.quitplayinsoundsjackass = true
				p.solchar.earlyburncancel = true
			end
			p.SolTransforming = $-1
		end
		
		--quick thing to make checking if you have all emeralds easier
		p.solchar.hasallemeralds = allSolEmeralds(p)	-- update to account for mpemeraldsave - snu
		if p.solchar.solexception 
			p.solchar.hasallemeralds = true
		end
		
		for fof in p.mo.subsector.sector.ffloors()
			if GetSecSpecial(fof.sector.special, 4) == 6 --and fof == p.mo.floorrover
				p.solchar.solexception = true
				
				if not p.solchar.istransformed 
					p.rings = 50
					p.solchar.istransformed = 2
					p.powers[pw_super] = 0
				end
			end
		end
		
		if solchars[p.mo.skin][2] == 1 or solchars[p.mo.skin][2] >= 2 and p.solchar.hasallemeralds and p.solchar.istransformed > 1
			p.charflags = $ & ~ SF_SUPER
			p.powers[pw_super] = 0
		end
		
		if p.solchar.emeraldcircle > 0
			if p.solchar.emeraldcircle == 1
				for i = 1, 7
					local e = P_SpawnMobjFromMobj(p.mo, 0, 0, 0, MT_TRANSFORM_SOLEMERALD)
					e.state = S_SOLEMERALD1+(i-1)
					e.ang = FixedAngle((360*FRACUNIT)/7)
					e.target = p.mo
					e.i = i
					P_TeleportMove(e, p.mo.x+P_ReturnThrustX(e, e.ang*i, 60*p.mo.scale), p.mo.y+P_ReturnThrustY(e, e.ang*i, 60*p.mo.scale), p.mo.z)
				end
			end
			p.solchar.emeraldcircle = $+1
		end
		
		--Blaze's burning form. Since you can activate it without all 7 chaos emeralds, it needs to be totally custom coded in.
		if (p.solchar.istransformed > 0)
			if not (p.solchar.earlyburncancel)
				if (p.solchar.musicplaying == 0) or (p.powers[pw_underwater] > TICRATE*11 and S_MusicName() == "_drown")
					P_PlayJingleMusic(p, "METAM", nil, true, JT_OTHER)
					p.solchar.musicplaying = 1
				end
				if p.solchar.burningcolor == 5
					p.solchar.bcolordirection = -1
				elseif
					p.solchar.burningcolor == 1
					p.solchar.bcolordirection = 1
				end
				if p.solchar.istransformed % 2 == 0
					p.solchar.burningcolor = $ + p.solchar.bcolordirection
				end
				if p.solchar.burningcolor > 5
					p.solchar.burningcolor = 1
				end
				if (p.mo.state < S_PLAY_SUPER_TRANS1) or (p.mo.state > S_PLAY_SUPER_TRANS5)
					--if p.mo.skin == "blaze"
					if not (solchars[p.mo.skin][2] == 3)
						if type(solchars[p.mo.skin][1]) == "number"
							p.mo.color = solchars[p.mo.skin][1]+(p.solchar.burningcolor-1)
						else
							p.mo.color = skins[p.mo.skin].supercolor+(p.solchar.burningcolor-1)
						end
					end
					--elseif p.mo.skin == "marine"
						--p.mo.color = _G["SKINCOLOR_AQUAMARINE" .. p.solchar.burningcolor]
					--end
				end
			end
			if p.mo.sprite2&~FF_SPR2SUPER != SPR2_TRNS
				p.solchar.istransformed = $ + 1
			elseif (p.mo.skin == "blaze")
				p.mo.blasstransformextra = $ or 0
				p.mo.blasstransforminc = $ or 0
				if p.mo.state == S_PLAY_SUPER_TRANS6
					p.mo.blasstransformextra = $+1 
					if p.mo.blasstransformextra % 2 == 0 and (p.mo.blasstransforminc <= 2)
						p.mo.state = $
						p.mo.blasstransforminc = $+1
					end
				end
			end
		end
		
		if p.solchar.istransformed > 1
			local bskin = skins[p.skin]
			if not (p.solchar.earlyburncancel)
				p.charflags = $&~SF_SUPER
			end
			if p.powers[pw_invulnerability] < 2
				p.powers[pw_invulnerability] = 1
			end
			if p.powers[pw_extralife] == 2
				p.powers[pw_extralife] = 0
				if S_MusicName(p, "_1UP")
					p.solchar.musicplaying = 0
				end
			end
			if S_MusicName == "_DROWN"
				p.solchar.musicplaying = 0
			end
			p.fakenormalspeed = bskin.normalspeed*5/3
			if not p.isxmomentum and not (p.mo.state == S_PLAY_ROLL) and not (p.blazespindash and p.blazespindash > 0)
				p.normalspeed = bskin.normalspeed*5/3
			end
			p.runspeed = bskin.runspeed*5/3
			if not (p.blazeboosting)
				p.jumpfactor = bskin.jumpfactor*4/3
			end
			p.shieldscale = 0
			if p.solchar.istransformed % TICRATE == 0
				p.rings = max(0, $-1)
			end
			if (p.mo.skin == "blaze")
				p.mindash = 60*FRACUNIT -- skins[p.mo.skin].maxdash/4
				p.maxdash = 80*FRACUNIT --+ skins[p.mo.skin].maxdash/2 + skins[p.mo.skin].maxdash/4
				if (P_IsObjectOnGround(p.mo) or (p.mo.eflags & MFE_JUSTHITFLOOR))
					p.burningblazethokked = false
				end
				if p.blazehoverpower and p.blazehovertimer
					p.blazehovertimer = 88
					if p.blazehoverpower < -15*FRACUNIT
						p.blazehoverpower = -15*FRACUNIT
					end
				end
				if (rusheffects.value) and (P_RandomRange(3, 0) == 0)
					local fire = P_SpawnMobjFromMobj(p.mo, P_RandomRange(-18, 18) * FRACUNIT, P_RandomRange(-18, 18) * FRACUNIT, P_RandomRange(0, 32) * FRACUNIT, MT_FLAMEPARTICLE)
					P_SetObjectMomZ(fire, P_RandomRange(1,3)*p.mo.scale, false)
					fire.scale = p.mo.scale
					--fire.color = p.mo.color
					--fire.colorized = true
				end		
			end
			p.powers[pw_sneakers] = 0
			if p.rings == 0 or p.solchar.earlyburncancel
				local bskin = skins[p.skin]
				p.solchar.earlyburncancel = false
				p.solchar.istransformed = 0
				p.solchar.solexception = false
				p.solchar.musicplaying = 0
				p.mo.blasstransformextra = 0
				p.mo.blasstransforminc = 0
				P_RestoreMusic(p)
				if not (bskin.flags & SF_RUNONWATER)
					p.charflags = $ & ~ SF_RUNONWATER
				end
				if not (bskin.flags & SF_NOSHIELDABILITY)
					p.charflags = $ & ~ SF_NOSHIELDABILITY
				end
				p.mo.eflags = $ & ~ MFE_FORCESUPER
				p.mo.state = $
				if not (p.powers[pw_super])
					p.mo.color = p.skincolor
				end
				if not p.isxmomentum
					p.normalspeed = bskin.normalspeed
				end
				p.runspeed = bskin.runspeed
				p.shieldscale = bskin.shieldscale
				if p.mo.skin == "blaze"
					p.mindash = bskin.mindash
					p.maxdash = bskin.maxdash
				end
				p.fakenormalspeed = bskin.normalspeed
				p.jumpfactor = bskin.jumpfactor
			end
		end
		p.solchar.spindown = (p.cmd.buttons & BT_SPIN)
		p.solchar.jumpdown = (p.cmd.buttons & BT_JUMP)
	end
end)

addHook("ShouldJingleContinue", function(player, musname)
    if musname != "metam" return end
    if (player.solchar and player.solchar.istransformed)
        return true
    else
        return false
    end
end)

local yourmom
addHook("PreThinkFrame", function(r)
	yourmom = S_GetMusicPosition()
end)

addHook("MusicChange", function(om, nm)
	if (gamestate != GS_LEVEL) return end
	if (consoleplayer and consoleplayer.solchar)
	and (consoleplayer.solchar.istransformed)
	and (nm != "metam")
		return "metam", 0, true, yourmom
	end
end)

addHook("JumpSpinSpecial", function(p)
	if not (p.mo and p.mo.valid and p.mo.health and p.mo.skin == "blaze" and p.solchar.istransformed
	and not (p.pflags & PF_SPINDOWN) and (p.blazeburstcharge >= TICRATE*2)) return end --and not p.burningblazethokked
	P_InstaThrust(p.mo, p.mo.angle, 60*p.mo.scale)
	p.mo.state = S_PLAY_RUN
	S_StartSound(p.mo, sfx_blzbst)
	p.blazeboosting = true
	p.burningblazethokked = true
	p.blazeboosttimer = 1
end)

addHook("PostThinkFrame", function(ass)
	for p in players.iterate
		if not (p.mo and p.mo.valid) continue end
		if solchars[p.mo.skin] and p.solchar and p.solchar.istransformed > 1
			if not (p.charflags & SF_NOSUPERSPRITES)
			and not (p.mo.eflags & MFE_FORCESUPER)
				p.mo.eflags = $ | MFE_FORCESUPER
				p.mo.state = $
			end
			p.mo.frame = $|FF_FULLBRIGHT
			if p.speed + abs(p.mo.momz) > FixedMul(20*FRACUNIT,p.mo.scale)
				local gmobj = P_SpawnGhostMobj(p.mo)
				gmobj.fuse = 2
				gmobj.spriteyoffset = p.mo.spriteyoffset
				if gmobj.tracer then gmobj.tracer.fuse = 2 end
				if (leveltime & 1)
					gmobj.frame = $&~FF_TRANSMASK
					gmobj.frame = $|tr_trans70<<FF_TRANSSHIFT
					if (gmobj.tracer)
						gmobj.tracer.frame = $&~FF_TRANSMASK
						gmobj.tracer.frame = $|tr_trans70<<FF_TRANSSHIFT
					end
				end
			end
			p.charflags = $|SF_RUNONWATER|SF_NOSHIELDABILITY
		end
		--gotta move this thing over here so it properly updates
		if p.mo.skin == "blaze" and p.blazeburstcharge >= TICRATE*2
			if (leveltime % 8 == 0)
				local spoopy = P_SpawnGhostMobj(p.mo)
				spoopy.spriteyoffset = p.mo.spriteyoffset
				spoopy.color = p.blazeflamecolor
				spoopy.colorized = true
				spoopy.blendmode = AST_ADD
				spoopy.destscale = p.mo.scale*5
				spoopy.scalespeed = p.mo.scale/28
				spoopy.fuse = 0
				spoopy.tics = 10
				spoopy.frame = $ & ~FF_TRANSMASK | TR_TRANS70
				spoopy.target = p.mo
				spoopy.BlazeSpecialGhost = true
				if spoopy.tracer
					spoopy.tracer.color = p.blazeflamecolor
					spoopy.tracer.colorized = true
					spoopy.tracer.blendmode = AST_ADD
					spoopy.tracer.destscale = p.mo.scale*5
					spoopy.tracer.scalespeed = p.mo.scale/28
					spoopy.tracer.fuse = 0
					spoopy.tracer.tics = 10
					spoopy.tracer.frame = $ & ~FF_TRANSMASK | TR_TRANS70
					spoopy.tracer.target = p.mo
					spoopy.tracer.BlazeSpecialGhost = true
					spoopy.tracer.FuckYouScoobyDoo = true
				end
			end
		end
	end
end)

addHook("MobjDamage", function(target, inflictor, source, damage, damagetype)
	if target.player and solchars[target.skin] and target.player.solchar and target.player.solchar.istransformed > 0 and not (damagetype&DMG_DEATHMASK)
		return true
	end
end, MT_PLAYER)

addHook("PlayerCanDamage", function(p, mo)
	if not (p.mo and p.mo.valid and p.mo.health and solchars[p.mo.skin] and p.solchar) return end
	if p.solchar.istransformed > 0 and not (mo.flags & MF_MONITOR)
		return true
	end
end)

addHook("MobjThinker", function(mo)
	if not (mo and mo.valid and mo.health) return end
	local i = mo.i
	if mo.target and mo.target.valid and mo.target.health
		if not (mo.lifetime)
			mo.lifetime = 1
		end
		if not (mo.lifetime2)
			if mo.lifetime < TICRATE*4
				mo.lifetime = $+4
			end
			if mo.lifetime >= TICRATE*4
				mo.lifetime2 = 1
				mo.lifetime2startup = 1
			end
			mo.ang = $+FixedAngle((FRACUNIT/8*mo.lifetime)/i)
		else
			mo.lifetime2startup = $+1
			if mo.lifetime2startup > TICRATE
				mo.lifetime2 = $+1
			else
				mo.ang = $+FixedAngle((FRACUNIT/8*mo.lifetime+mo.lifetime2startup)/i)
			end
			if mo.lifetime2 > 1
				if mo.lifetime2 > TICRATE-TICRATE/4
					mo.lifetime = max($-45, 0)
				elseif mo.lifetime2 <= TICRATE/6
					mo.lifetime = $+25
				end
				if mo.lifetime2 == 2
					mo.target.OkDoTheSoundNowPlease = sfx_s3k46
				end
			end
		end
		if (mo.target.player.CancelSolTransformAnimation) and not (mo.lifetime2)
			mo.lifetime2startup = TICRATE*2
			mo.lifetime2 = 1
			mo.lifetime = TICRATE*4
			mo.target.quitplayinsoundsjackass = true
		end
		mo.poggers = (mo.target.player.cmd.buttons & BT_JUMP)
		local ghost = P_SpawnGhostMobj(mo)
		ghost.fuse = 2
		if P_MobjFlip(mo.target) == 1
			P_TeleportMove(mo, mo.target.x+P_ReturnThrustX(mo, mo.ang*i, mo.lifetime/2*mo.target.scale), mo.target.y+P_ReturnThrustY(mo, mo.ang*i, mo.lifetime/2*mo.target.scale), mo.target.z+mo.target.height/2)
		else
			P_TeleportMove(mo, mo.target.x+P_ReturnThrustX(mo, mo.ang*i, mo.lifetime/2*mo.target.scale), mo.target.y+P_ReturnThrustY(mo, mo.ang*i, mo.lifetime/2*mo.target.scale), mo.target.z+mo.target.height-mo.height-mo.target.height/2)
		end
		if (rusheffects.value) and (P_RandomRange(1, 4) == 1)
			local ass = P_SpawnMobjFromMobj(mo, P_RandomRange(30, -30)*FRACUNIT, P_RandomRange(30, -30)*FRACUNIT, P_RandomRange(30, -30)*FRACUNIT, MT_BOXSPARKLE)
			ass.colorized = true
			ass.color = bullshit[mo.state]
			ass.frame = $ | FF_FULLBRIGHT
			if P_RandomRange(0, 2) != 0
				ass.scale = mo.scale
			else
				ass.scale = mo.scale - mo.scale/4
			end
		end
		
		mo.target.tics = 2
		mo.target.anim_duration = 2
			
		mo.target.player.SolTransforming = 2
		if (mo.target.player.CancelSolTransform)
			local ilikeyacutg = P_SpawnGhostMobj(mo)
			ilikeyacutg.momz = 10*FRACUNIT*P_MobjFlip(mo.target)
			P_RemoveMobj(mo)
			return 
		end
		if mo.lifetime == 0
			P_FlashPal(mo.target.player, PAL_WHITE, 3)
			mo.target.OkDoTheSoundNowPlease = sfx_supert
			P_RemoveMobj(mo)
		end
	else
		P_RemoveMobj(mo)
	end
end, MT_TRANSFORM_SOLEMERALD)

addHook("MobjSpawn", function(sparkle)
	if not (sparkle.destscale) return end
	for player in players.iterate
		if (player.powers[pw_invulnerability] != 1) continue end
		if (sparkle.x != player.mo.x) or (sparkle.y != player.mo.y) continue end
		sparkle.flags = $|MF2_DONTDRAW
	end
end, MT_IVSP)
